<?php

/**
 * 安心站长axzz.cn，一个追求技术创造者。让网站、小程序、APP、应用程序牛起来……
 *
 * cighsen02 365182575
 * 
 * Create at: 2022.12.29
 * Update at: 2025.09.16
 */

namespace Twoir\System\Support;

use Exception;

use function array_filter;
use function array_keys;
use function array_intersect;
use function array_replace;
use function array_values;
use function count;
use function in_array;
use function is_string;
use function sprintf;
use function str_contains;
use function str_replace;

/**
 * 两张表存放模型 both
 */
trait BothTrait
{
    /**
     * 字段默认值类型
     *
     * @return array
     */
    public static function fieldType(): array
    {
        $default = array_replace(static::MYDEFAULT['pri'], static::MYDEFAULT['sec']);
        // 字段类型
        foreach($default as &$value) {

            $value = null === $value || 0 === $value ? 'int' : 'string';
        }

        return $default;
    }

    /**
     * 统计数量
     *
     * @param array $where
     * @param string $index
     * @return int
     * 
     * @throws Exception
     */
    public static function getCount(?array $where = [], string $index = 'pri'): int
    {
        global $empire, $dbtbpre;

        list($str_where, $data) = static::parseWhere($where);

        if (!in_array($index, ['pri', 'sec'])) throw new Exception('参数错误', 9200);

        if (
            'pri' === $index
            && !array_intersect(static::FIELD['pri'], array_keys($data))
            && array_intersect(static::FIELD['sec'], array_keys($data))
        ) {
            $index = 'sec';
        }

        $sql = sprintf('SELECT count(*) as total FROM %s%s', $dbtbpre . static::TABLE[$index], $str_where);

        return (int)$empire->gettotal($sql);
    }

    /**
     * 拉取一条数据
     *
     * @param array $where 键值数组 $field 值数组
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @return array
     */
    public static function getOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = ''): array
    {
        $result = static::getList($where, $field, $groupby, $orderby, 1, 0);

        return $result ? array_values($result)[0] : [];
    }

    /**
     * 读取多条(列表), 只取主表
     *
     * @param array $where
     * @param array $field
     * @param string $groupby
     * @param string $orderby
     * @param int $limit
     * @param int $pagesize
     * @param int $page
     * @return array
     * 
     * @throws Exception
     */
    public static function getList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0): array
    {
        global $empire, $dbtbpre;

        list($str_where, $data) = static::parseWhere($where);

        $get_field  = [];
        $arr_where 	= [
            'pri' => [],
            'sec' => []
        ];

        if (['*'] !== $field && count($data) > 0) {

            $where_field = array_keys($data);
            $arr_where = [
                'pri' => array_filter(static::FIELD['pri'], fn($item) => in_array($item, $where_field)),
                'sec' => array_filter(static::FIELD['sec'], fn($item) => in_array($item, $where_field))
            ];
        }

        $str_group	= null === $groupby || '' === $groupby ? '' : ' GROUP BY ' . $groupby;
        $str_order	= null === $orderby || '' === $orderby ? '' : ' ORDER BY ' . $orderby;
        $str_limit 	= 0 === $limit && 0 === $pagesize ? '' : ($limit > 0 ? ' LIMIT ' . $limit : ' LIMIT ' . ($page * $pagesize) . ',' . $pagesize);

        $option = [];
        $get_field_is_empty = true;
        foreach(static::TABLE as $t => $table) {

            $get_field  = ['*'] === $field ? static::FIELD[$t] : array_intersect(static::FIELD[$t], $field);
            $count = count($get_field);

            if (
                ['*'] === $field 
                || $count > 1 
                || (1 === $count && [static::PRIMARY] !== $get_field)
                || (count($arr_where[$t]) > 0 && [static::PRIMARY] !== array_values($arr_where[$t]))
            ) {

                $option[$t] = [
                    'field'     => $get_field,
                    'table'     => $dbtbpre . $table,
                    'where'     => $str_where,
                    'group'		=> $str_group,
                    'order'     => $str_order,
                    'limit'     => $str_limit
                ];

                if ($get_field_is_empty) $get_field_is_empty = false;
            }
        }

        if ($get_field_is_empty && count($field) > 0) throw new Exception('字段不存在', 9200);

        // 主表
        if (isset($option['pri']) && !isset($option['sec'])) {

            $sql = sprintf('SELECT %s FROM %s%s%s%s%s',
                '`' . implode('`,`', $option['pri']['field']) . '`',
                $option['pri']['table'],
                $option['pri']['where'],
                $option['pri']['group'],
                $option['pri']['order'],
                $option['pri']['limit']
            );
        }
        // 副表
        elseif (!isset($option['pri']) && isset($option['sec'])) {

            $sql = sprintf('SELECT %s FROM `%s`%s%s%s%s',
                '`' . implode('`,`', $option['sec']['field']) . '`',
                $option['sec']['table'],
                $option['sec']['where'],
                $option['sec']['group'],
                $option['sec']['order'],
                $option['sec']['limit']
            );
        }
        // 并表
        else{

            // 去重
            $option['sec']['field'] = array_diff($option['sec']['field'], [static::PRIMARY]);
            $sql = sprintf(
                'SELECT %s FROM `%s` AS a INNER JOIN `%s` AS b ON %s%s%s%s%s',
                ($option['pri']['field'] ? 'a.`' . implode('`,a.`', $option['pri']['field']) . '`' : '') . ($option['pri']['field'] && $option['sec']['field'] ? ',' : '') . ($option['sec']['field'] ? 'b.`' . implode('`,b.`', $option['sec']['field']) . '`' : ''),
                $option['pri']['table'],
                $option['sec']['table'],
                'a.`' . static::PRIMARY . '` = b.`' . static::PRIMARY . '`',
                count($data) > 0 ? static::addPrefix($option['pri']['where']) : '',
                $option['pri']['group'] ? static::addPrefix($option['pri']['group']) : '',
                $option['pri']['order'] ? static::addPrefix($option['pri']['order']) : '',
                $option['pri']['limit']
            );
        }

        $sql = $empire->query($sql);
        $result = [];
        if(0 < $empire->num1($sql)){

            while($r = $empire->fetch($sql)){
                // 去数字键
                $result[] = array_filter($r, fn($key) => is_string($key), ARRAY_FILTER_USE_KEY);
            }
        }

        return $result;
    }

    /**
     * 插入交易信息
     *
     * @param array $add
     * @param string $type
     * @return int 0为失败，成功返回当前id
     */
    public static function insert(array $add = [], string $type = ''): int
    {
        global $empire, $dbtbpre;

        $insert = static::MYDEFAULT;
        // 处理
        foreach($insert as $key => &$value) {
            // 分组
            $value  = array_filter($value, fn($item) => in_array($item, static::FIELD[$key]), ARRAY_FILTER_USE_KEY);
            $add    = array_filter($add, fn($item) => in_array($item, static::FIELD[$key]), ARRAY_FILTER_USE_KEY);
            // 更新
            $value 	= array_replace($value, $add);
        }

        // 移除主键, 自动增长
        if (!isset($add[static::PRIMARY])) unset($insert['index'][static::PRIMARY]);

        // 主表
        list($key, $value) = static::parseInsert($insert['pri']);
        $sql 	= sprintf('INSERT %sINTO `%s`(%s) VALUES(%s)', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . static::TABLE['pri'], $key, $value);
        $sqlpri = $empire->query($sql);

        $lastid = $empire->lastid();

        // 副表
        $insert['sec'][static::PRIMARY] = $lastid;
        list($key, $value) = static::parseInsert($insert['sec']);
        $sql 	= sprintf('INSERT %sINTO `%s`(%s) VALUES(%s)', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . static::TABLE['sec'], $key, $value);
        $sqlsec = $empire->query($sql);

        // 更新失败
        if (!$sqlpri || !$sqlsec) $lastid = 0;

        return $lastid;
    }

    /**
     * 更新数据
     *
     * @param array $where
     * @param array $add
     * @param int $limit
     * @param string $type
     * @return bool
     */
    public static function update(array $where = [], array $add = [], int $limit = 0, string $type = ''): bool
    {
        global $empire, $dbtbpre;

        list($str_where) = static::parseWhere($where);

        // 去null值, 0值或负值不能去，
        $add    = array_filter($add, fn($item) => null !== $item && !empty($item));
        // 找出要新字段进行更新
        $field  = array_keys($add);
        $sqlpri = $sqlsec = false;
        $limit  = $limit ? ' LIMIT ' . $limit : '';

        // 更新主表
        $update = array_intersect(static::FIELD['pri'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s`%s` SET %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . static::TABLE['pri'], static::parseUpdate($put_data), $str_where, $limit);
            $sqlpri     = $empire->query($sql);
        }

        // 更新副表
        $update = array_intersect(static::FIELD['sec'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s`%s` SET %s%s%s', static::$low_priority ? 'LOW_PRIORITY ' : '', $dbtbpre . static::TABLE['sec'], static::parseUpdate($put_data), $str_where, $limit);
            $sqlsec     = $empire->query($sql);
        }

        return false !== $sqlpri || false !== $sqlsec;
    }

    /**
     * 联表名增加前缀
     *
     * @param string $str
     * @return string
     */
    private static function addPrefix(string $str): string
    {
        foreach (static::FIELD['pri'] as $field) {

            if (str_contains($str, '`' . $field . '`')) $str = str_replace('`' . $field . '`', 'a.`' . $field . '`', $str);
        }

        foreach (static::FIELD['sec'] as $field) {

            if ($field !== static::PRIMARY && str_contains($str, '`' . $field . '`')) $str = str_replace('`' . $field . '`', 'b.`' . $field . '`', $str);
        }

        return $str;
    }
}
